#include "netcamera.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QMessageBox>
#include <QPixmap>
#include <QFileDialog>

NetCamera::NetCamera(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);

    m_client = new QMqttClient(this);

    connect(ui.btn_test, SIGNAL(clicked()), this, SLOT(btn_test_clicked()));
    connect(ui.btn_connect, SIGNAL(clicked()), this, SLOT(btn_connect_clicked()));
    connect(ui.btn_discovery, SIGNAL(clicked()), this, SLOT(btn_discovery_clicked()));
    connect(ui.btn_snap, SIGNAL(clicked()), this, SLOT(btn_snap_clicked()));
    connect(ui.btn_save, SIGNAL(clicked()), this, SLOT(btn_save_clicked()));
    connect(ui.btn_reboot, SIGNAL(clicked()), this, SLOT(btn_reboot_clicked()));
    connect(ui.btn_format, SIGNAL(clicked()), this, SLOT(btn_format_clicked()));
    connect(ui.btn_list, SIGNAL(clicked()), this, SLOT(btn_list_clicked()));
    connect(ui.btn_refresh, SIGNAL(clicked()), this, SLOT(btn_refresh_clicked()));
    connect(m_client, SIGNAL(connected()), this, SLOT(mqtt_connected()));
    connect(m_client, SIGNAL(disconnected()), this, SLOT(mqtt_disconnected()));

    mqtt_offline_stat();
    layout_cam = new QGridLayout(this);
    layout_pic = new QGridLayout(this);
    layout_cam->setAlignment(Qt::AlignTop);
    layout_pic->setAlignment(Qt::AlignTop);
    sc_widget = new QWidget(this);
    sp_widget = new QWidget(this);
    sc_widget->setLayout(layout_cam);
    sp_widget->setLayout(layout_pic);
    ui.sa_cam->setWidget(sc_widget);
    ui.sa_pic->setWidget(sp_widget);
    prog_bar = new QProgressBar();
    ui.statusBar->addPermanentWidget(prog_bar);
    prog_bar->setMaximum(100);
    prog_bar->setMinimum(0);
    pic_indicator = new QLabel("picture indicator");
    ui.statusBar->addWidget(pic_indicator);
    timer = new QTimer();
    btn_new_task = nullptr;
    connect(timer, SIGNAL(timeout()), this, SLOT(timer_timeout()));
    ui.pic->hide();
}

void NetCamera::timer_timeout()
{
    timer->stop();
    this->setEnabled(true);
    prog_bar->setMaximum(100);
    QMessageBox::warning(this, "Timeout", "Request Timeout!", QMessageBox::Ok);
}

void NetCamera::btn_test_clicked()
{
    QString file = QFileDialog::getOpenFileName(this, "Choose a picture", ".", "Image Files(*.jpg)", nullptr, 0);
    qDebug() << "file: " << file;
    QImage img;
    img.load(file);
    cur_img = img;
    ui.pic->resize(ui.pic_widget->size());
    ui.pic->setPixmap(QPixmap::fromImage(img).scaled(ui.pic_widget->size(), Qt::KeepAspectRatio));
}
void NetCamera::btn_connect_clicked()
{
    qDebug() << "btn_connect_clicked";
    if (m_client->state() == QMqttClient::Connected) {
        m_client->disconnectFromHost();
    }
    else {
        m_client->disconnectFromHost();
        m_client->setHostname(ui.ip->text());
        m_client->setPort(ui.port->text().toInt());
        m_client->setUsername(ui.username->text());
        m_client->setPassword(ui.passwd->text());
        m_client->connectToHost();
    }
}
void NetCamera::btn_discovery_clicked()
{
    qDebug() << "btn_discovery_clicked\n";
    QJsonObject jb_msg, jb_params;
    jb_params.insert("code", QJsonValue("0x01"));
    jb_params.insert("param", QJsonObject());
    jb_msg.insert("params", jb_params);
    jb_msg.insert("ecode", QJsonValue(0));
    m_client->publish(QMqttTopicName(QString("/ter/query/discovery")), QJsonDocument(jb_msg).toJson(), 1, false);
}
void NetCamera::btn_snap_clicked()
{
    QJsonObject jb_msg, jb_params;
    jb_params.insert("code", QJsonValue("0x23"));
    jb_params.insert("param", QJsonObject());
    jb_msg.insert("params", jb_params);
    jb_msg.insert("ecode", QJsonValue(0));
    m_client->publish(QMqttTopicName("/ter/" + active_sn + "/request"), QJsonDocument(jb_msg).toJson(), 1, false);
    this->set_busy();
}
void NetCamera::btn_save_clicked()
{
    if (cur_img.isNull()) {
        return;
    }
    QString filename = QFileDialog::getSaveFileName(this, \
        "Save Current Picture", \
        ".", \
        tr("Image Files(*.jpg)"), \
        0);
    cur_img.save(filename);
}
void NetCamera::btn_reboot_clicked()
{
    QJsonObject jb_msg, jb_params;
    jb_params.insert("code", QJsonValue("0x21"));
    jb_params.insert("param", QJsonObject());
    jb_msg.insert("params", jb_params);
    jb_msg.insert("ecode", QJsonValue(0));
    m_client->publish(QMqttTopicName("/ter/" + active_sn + "/request"), QJsonDocument(jb_msg).toJson(), 1, false);
    this->set_busy();
}
void NetCamera::btn_format_clicked()
{
    QJsonObject jb_msg, jb_params;
    jb_params.insert("code", QJsonValue("0x22"));
    jb_params.insert("param", QJsonObject());
    jb_msg.insert("params", jb_params);
    jb_msg.insert("ecode", QJsonValue(0));
    m_client->publish(QMqttTopicName("/ter/" + active_sn + "/request"), QJsonDocument(jb_msg).toJson(), 1, false);
    this->set_busy();
}
void NetCamera::btn_list_clicked()
{
    if (ui.btn_list->text() == "PictureList") {
        this->delete_all_picture();
        ui.btn_list->setText("TaskList");
    }
    else {
        this->delete_all_task();
        ui.btn_list->setText("PictureList");
    }
}
void NetCamera::btn_refresh_clicked()
{
    QJsonObject jb_msg, jb_params;
    if (ui.btn_list->text() == "PictureList") {
        this->delete_all_picture();
        qDebug() << "picturelist";
    }
    else {
        this->delete_all_task();
        jb_params.insert("code", "0x25");
        jb_params.insert("param", QJsonObject());
        jb_msg.insert("params", jb_params);
        jb_msg.insert("ecode", QJsonValue(0));
        m_client->publish(QMqttTopicName("/ter/" + active_sn + "/request"), QJsonDocument(jb_msg).toJson(), 1, false);
        this->set_busy();
    }
}

void NetCamera::mqtt_connected()
{
    mqtt_online_stat();
    disc_sub = m_client->subscribe(QMqttTopicFilter("/dev/response/discovery"), 1);
    will_sub = m_client->subscribe(QMqttTopicFilter("/dev/response/will"), 1);
    connect(disc_sub, SIGNAL(messageReceived(QMqttMessage)), this, SLOT(disc_msg_rcv(QMqttMessage)));
    connect(will_sub, SIGNAL(messageReceived(QMqttMessage)), this, SLOT(will_msg_rcv(QMqttMessage)));
}
void NetCamera::mqtt_disconnected()
{
    mqtt_offline_stat();
}

void NetCamera::mqtt_offline_stat()
{
    ui.ip->setEnabled(true);
    ui.port->setEnabled(true);
    ui.username->setEnabled(true);
    ui.passwd->setEnabled(true);
    ui.btn_connect->setStyleSheet("background:rgb(255,0,0)");
    ui.btn_connect->setText("Disconnected");

    dev_offline_stat();
    ui.sa_cam->setEnabled(false);
    ui.sa_pic->setEnabled(false);
    ui.btn_discovery->setEnabled(false);
    ui.btn_snap->setEnabled(false);
    ui.btn_reboot->setEnabled(false);
    ui.btn_format->setEnabled(false);
    ui.btn_list->setEnabled(false);
    ui.btn_refresh->setEnabled(false);
}
void NetCamera::mqtt_online_stat()
{
    ui.sa_cam->setEnabled(true);
    ui.sa_pic->setEnabled(true);
    ui.btn_discovery->setEnabled(true);
    ui.btn_snap->setEnabled(false);
    ui.btn_reboot->setEnabled(false);
    ui.btn_format->setEnabled(false);
    ui.btn_list->setEnabled(false);
    ui.btn_refresh->setEnabled(false);
    ui.btn_connect->setStyleSheet("background:rgb(0,255,0)");
    ui.btn_connect->setText("Connected");

    ui.ip->setEnabled(false);
    ui.port->setEnabled(false);
    ui.username->setEnabled(false);
    ui.passwd->setEnabled(false);
}
void NetCamera::dev_offline_stat()
{
    ui.btn_snap->setEnabled(false);
    ui.btn_list->setEnabled(false);
    ui.btn_refresh->setEnabled(false);
    ui.btn_reboot->setEnabled(false);
    ui.btn_format->setEnabled(false);
}
void NetCamera::dev_online_stat()
{
    ui.btn_snap->setEnabled(true);
    ui.btn_list->setEnabled(true);
    ui.btn_refresh->setEnabled(true);
    ui.btn_reboot->setEnabled(true);
    ui.btn_format->setEnabled(true);
}

void NetCamera::set_busy()
{
    this->setEnabled(false);
    prog_bar->setMaximum(0);
    timer->start(5000);
}

void NetCamera::set_idle()
{
    timer->stop();
    prog_bar->setMaximum(100);
    this->setEnabled(true);
}

Camera* NetCamera::find_cam(QString sn)
{
    Camera* tag_cam = nullptr;
    QList<Camera*> camlist = sc_widget->findChildren<Camera*>();
    for (int i = 0; i < camlist.size(); i++) {
        QString msn, ip, mac;
        Camera* cam = camlist.at(i);
        cam->get_cam_info(&msn, &ip, &mac);
        if (sn == msn) {
            tag_cam = cam;
            break;
        }
    }
    return tag_cam;
}

void NetCamera::delete_all_task(void)
{
    Task* task = nullptr;
    QList<Task*> task_list = sp_widget->findChildren<Task*>();
    for (int i = 0; i < task_list.size(); i++) {
        task = task_list.at(i);
        task->deleteLater();
    }
    if (btn_new_task != nullptr) {
        btn_new_task->deleteLater();
        btn_new_task = nullptr;
    }
}

void NetCamera::delete_all_picture(void)
{
    Picture* pic = nullptr;
    QList<Picture*> pic_list = sp_widget->findChildren<Picture*>();
    for (int i = 0; i < pic_list.size(); i++) { 
        pic = pic_list.at(i);
        pic->deleteLater();
    }
    if (btn_new_task != nullptr) {
        btn_new_task->deleteLater();
        btn_new_task = nullptr;
    }
}

void NetCamera::disc_msg_rcv(QMqttMessage msg)
{
    QString sn, ip, mac;
    qDebug() << "top: " << msg.topic().name();
    qDebug() << "payload: " << msg.payload();
    QJsonDocument jd = QJsonDocument::fromJson(msg.payload());
    QJsonObject jb = jd.object();
    if (jb.contains("params")) {
        QJsonObject jb_params = jb.value("params").toObject();
        if (jb_params.contains("code")) {
            QString code = jb_params.value("code").toString();
            if (code != "0x01") {
                return;
            }
            sn = jb_params.value("sn").toString();
            ip = jb_params.value("param").toObject().value("ip").toString();
            mac = jb_params.value("param").toObject().value("mac").toString();
            if (find_cam(sn) == nullptr) {
                qDebug() << "add new cam";
                Camera* cam = new Camera(this, sn, ip, mac);
                connect(cam, SIGNAL(btn_activate_clicked(bool, QString)), this, SLOT(cam_activate(bool, QString)));
                layout_cam->addWidget(cam);
            }
        }
    }
}
void NetCamera::will_msg_rcv(QMqttMessage msg)
{
    QString sn;
    qDebug() << "top: " << msg.topic().name();
    qDebug() << "payload: " << msg.payload();
    QJsonDocument jd = QJsonDocument::fromJson(msg.payload());
    QJsonObject jb = jd.object();
    if (jb.contains("params")) {
        QJsonObject jb_params = jb.value("params").toObject();
        if (jb_params.contains("code")) {
            QString code = jb_params.value("code").toString();
            if (code != "0x11") {
                return;
            }
        }
    }
}
void NetCamera::sn_resp_msg_rcv(QMqttMessage msg)
{
    qDebug() << "topic: " << msg.topic().name();
    qDebug() << "payload: " << msg.payload();
    QJsonDocument jd = QJsonDocument::fromJson(msg.payload());
    QJsonObject jb = jd.object();
    this->set_idle();
    if (jb.value("ecode").toInt() != 0) {
        QMessageBox::warning(this, "Error", "Request Error!", QMessageBox::Ok);
        return;
    }
    if (jb.contains("params")) {
        QJsonObject jb_params = jb.value("params").toObject();
        if (jb_params.contains("sn")) {
            QString sn = jb_params.value("sn").toString();
            if (sn == active_sn) {
                QString code = jb_params.value("code").toString();
                QJsonObject jb_param = jb_params.value("param").toObject();
                int icode = code.toInt(nullptr, 16);
                switch (icode) {
                case 0x21:      //reboot response
                    sn_reboot_resp(jb_param);
                    break;
                case 0x22:      //sd-format
                    sn_format_resp(jb_param);
                    break;
                case 0x23:      //snap response
                    sn_snap_resp(jb_param);
                    break;
                case 0x24:      //set task response
                    sn_taskset_resp(jb_param);
                    break;
                case 0x25:      //query task response
                    sn_taskget_resp(jb_param);
                    break;
                case 0x26:      //delete task response
                    sn_taskdel_resp(jb_param);
                    break;
                default:
                    break;
                }
            }
        }
    }
}
void NetCamera::sn_event_msg_rcv(QMqttMessage msg)
{
    QString type_str;
    qDebug() << "topic: " << msg.topic().name();
    qDebug() << "payload: " << msg.payload();
    QJsonDocument jd = QJsonDocument::fromJson(msg.payload());
    QJsonObject jb = jd.object();
    if (jb.value("ecode").toInt() != 0) {
        return;
    }
    if (jb.contains("params")) {
        QJsonObject jb_params = jb.value("params").toObject();
        if (jb_params.contains("sn")) {
            QString sn = jb_params.value("sn").toString();
            if (sn == active_sn) {
                QJsonObject jb_param = jb_params.value("param").toObject();
                int type = jb_param.value("type").toInt();
                int time = jb_param.value("time").toInt();
                QString pic = jb_param.value("pic").toString();
                QByteArray pic_data = QByteArray::fromBase64(pic.toLatin1());
                cur_img = QImage::fromData(pic_data);
                QPixmap img = QPixmap::fromImage(cur_img);

                if (img.isNull()) {
                    return;
                }
                ui.pic->resize(ui.pic_widget->size());
                ui.pic->setPixmap(img.scaled(ui.pic_widget->size(), Qt::KeepAspectRatio));
                ui.pic->show();
                if (type == 2) {
                    type_str = "Timed-Task";
                }
                else {
                    type_str = "Key-Button";
                }
                Picture* picture = new Picture(this, cur_img, type_str, time);
                pic_indicator->setText(picture->get_title());
                if (ui.btn_list->text() == "PictureList") {
                    connect(picture, SIGNAL(show_list_pic(Picture*)), this, SLOT(show_another_pic(Picture*)));
                    layout_pic->addWidget(picture);
                }
                else {
                    picture->deleteLater();
                }
            }
        }
    }
}

void NetCamera::cam_activate(bool activate, QString sn)
{
    if (activate == true) {
        if (active_sn != sn) {
            Camera* cam = find_cam(active_sn);
            if (cam != nullptr) {
                cam->deactive();
                m_client->unsubscribe(QMqttTopicFilter("/dev/" + sn + "/response"));
                m_client->unsubscribe(QMqttTopicFilter("/dev/" + sn + "/event"));
            }
        }
        active_sn = sn;
        QMqttSubscription* sn_response = m_client->subscribe(QMqttTopicFilter("/dev/" + sn + "/response"), 1);
        QMqttSubscription* sn_event = m_client->subscribe(QMqttTopicFilter("/dev/" + sn + "/event"), 1);
        qDebug() << "topic1: " << sn_response->topic();
        qDebug() << "topic2: " << sn_event->topic();
        connect(sn_response, SIGNAL(messageReceived(QMqttMessage)), this, SLOT(sn_resp_msg_rcv(QMqttMessage)));
        connect(sn_event, SIGNAL(messageReceived(QMqttMessage)), this, SLOT(sn_event_msg_rcv(QMqttMessage)));
        this->dev_online_stat();
    }
    else {
        active_sn = "";
        m_client->unsubscribe(QMqttTopicFilter("/dev/" + sn + "/response"));
        m_client->unsubscribe(QMqttTopicFilter("/dev/" + sn + "/event"));
        this->dev_offline_stat();
    }
}

void NetCamera::task_del(int time)
{
    QJsonObject jb_msg, jb_params, jb_param;
    QJsonArray list;
    list.append(QJsonValue(time));
    jb_param.insert("total", QJsonValue(1));
    jb_param.insert("time_list", list);
    jb_params.insert("code", QJsonValue("0x26"));
    jb_params.insert("param", jb_param);
    jb_msg.insert("params", jb_params);
    jb_msg.insert("ecode", QJsonValue(0));
    m_client->publish(QMqttTopicName("/ter/" + active_sn + "/request"), QJsonDocument(jb_msg).toJson(), 1, false);
    this->set_busy();
}

void NetCamera::btn_new_task_clicked()
{
    task_edit = new NewTaskEdit(this);
    connect(task_edit, SIGNAL(edit_ok_clicked(int)), this, SLOT(edit_ok_clicked(int)));
    connect(task_edit, SIGNAL(edit_cancel_clicked()), this, SLOT(edit_cancel_clicked()));
    task_edit->setModal(true);
    task_edit->show();
}

void NetCamera::edit_ok_clicked(int time)
{
    task_edit->deleteLater();
    QJsonObject jb_msg, jb_params, jb_param;
    QJsonArray ja;
    ja.append(QJsonValue(time));
    jb_param.insert("total", QJsonValue(1));
    jb_param.insert("time_list", ja);
    jb_params.insert("code", QJsonValue("0x24"));
    jb_params.insert("param", jb_param);
    jb_msg.insert("params", jb_params);
    jb_msg.insert("ecode", QJsonValue(0));
    m_client->publish(QMqttTopicName("/ter/" + active_sn + "/request"), QJsonDocument(jb_msg).toJson(), 1, false);
    this->set_busy();
}

void NetCamera::edit_cancel_clicked()
{
    task_edit->deleteLater();
}

void NetCamera::show_another_pic(Picture *pic)
{
    cur_img = pic->get_image();
    pic_indicator->setText(pic->get_title());
    ui.pic->resize(ui.pic_widget->size());
    ui.pic->setPixmap(QPixmap::fromImage(cur_img).scaled(ui.pic_widget->size(), Qt::KeepAspectRatio));
    ui.pic->show();
}

void NetCamera::sn_reboot_resp(QJsonObject jb)
{
    int action = jb.value("action").toInt();
    if (action == 1) {
        QMessageBox::information(this, "Reboot", "Succeess!", QMessageBox::Ok);
    }
    else {
        QMessageBox::warning(this, "Reboot", "Failed!", QMessageBox::Ok);
    }
}
void NetCamera::sn_format_resp(QJsonObject jb)
{
    int action = jb.value("result").toInt();
    if (action == 1) {
        QMessageBox::information(this, "Format SD Card", "Succeess!", QMessageBox::Ok);
    }
    else {
        QMessageBox::warning(this, "Format SD Card", "Failed!", QMessageBox::Ok);
    }
}
void NetCamera::sn_snap_resp(QJsonObject jb)
{
    int action = jb.value("result").toInt();
    if (action != 1)  {
        QMessageBox::warning(this, "Snapshot", "Failed!", QMessageBox::Ok);
        return;
    }
    int time = jb.value("time").toInt();
    QString pic = jb.value("pic").toString();
    QByteArray pic_data = QByteArray::fromBase64(pic.toLatin1());
    cur_img = QImage::fromData(pic_data);
    QPixmap img = QPixmap::fromImage(cur_img);

    if (img.isNull()) {
        QMessageBox::warning(this, "Snapshot", "No Valid picture !", QMessageBox::Ok);
        return;
    }

    ui.pic->resize(ui.pic_widget->size());
    ui.pic->setPixmap(img.scaled(ui.pic_widget->size(), Qt::KeepAspectRatio));
    ui.pic->show();
    Picture* picture = new Picture(this, cur_img, "Real-Time", time);
    pic_indicator->setText(picture->get_title());

    if (ui.btn_list->text() == "PictureList") {
        connect(picture, SIGNAL(show_list_pic(Picture*)), this, SLOT(show_another_pic(Picture*)));
        layout_pic->addWidget(picture);
    }
    else {
        picture->deleteLater();
    }
}
void NetCamera::sn_taskset_resp(QJsonObject jb)
{
    int result = jb.value("result").toInt();
    if (result == 1) {
        QMessageBox::information(this, "New Snapshot Task", "Succeess!", QMessageBox::Ok);
    }
    else {
        QMessageBox::warning(this, "New Snapshot Task", "Failed!", QMessageBox::Ok);
    }
    ui.btn_refresh->clicked();
}
void NetCamera::sn_taskget_resp(QJsonObject jb)
{
    int tl[1024];
    int result = jb.value("result").toInt();
    if (result != 1) {
        QMessageBox::warning(this, "New Snapshot Task", "Failed!", QMessageBox::Ok);
    }
    QJsonArray time_list = jb.value("time_list").toArray();
    for (int i = 0; i < time_list.count(); i++) {
        tl[i] = time_list.at(i).toInt();
    }
    for (int i = 0; i < time_list.count(); i++) {
        for (int j = i; j < time_list.count(); j++) {
            if (tl[i] > tl[j]) {
                int tmp = tl[i];
                tl[i] = tl[j];
                tl[j] = tmp;
            }
        }
    }
    for (int i = 0; i < time_list.count(); i++) {
        Task* tsk = new Task(this, tl[i]);
        layout_pic->addWidget(tsk);
        connect(tsk, SIGNAL(btn_del_clicked(int)), this, SLOT(task_del(int)));
    }
    if (btn_new_task == nullptr) {
        btn_new_task = new QPushButton("New Task");
    }
    layout_pic->addWidget(btn_new_task);
    connect(btn_new_task, SIGNAL(clicked()), this, SLOT(btn_new_task_clicked()));
}
void NetCamera::sn_taskdel_resp(QJsonObject jb)
{
    int result = jb.value("result").toInt();
    if (result == 1) {
        QMessageBox::information(this, "Delete Snapshot Task", "Succeess, Please Click [Refresh] Button to Update List!", QMessageBox::Ok);
    }
    else {
        QMessageBox::warning(this, "Delete Snapshot Task", "Failed!", QMessageBox::Ok);
    }
}

void NetCamera::resizeEvent(QResizeEvent* event)
{
    QWidget::resizeEvent(event);
    QSize sz = ui.pic_widget->size();
    ui.pic->resize(sz);
    sz.setWidth(sz.width() - 5);
    sz.setHeight(sz.height() - 5);
    ui.pic->setPixmap(QPixmap::fromImage(cur_img).scaled(sz, Qt::KeepAspectRatio));
}
